/**
 * Mock Request Router
 * @author Wangtd
 */
const express = require('express');
const router = express.Router();
const fs = require('fs');
const path = require('path');
const httpRequest = require('request');
const multipart = require('connect-multiparty');
const logger = require('./logger.js');
const config = require('../config.js');

router.post('/module/get', function(request, response) {
    var filedir = path.join(__dirname, config.mockDir);
    var files = fs.readdirSync(filedir);
    var modules = [];
    for (var i in files) {
        var filename = files[i];
        var file = fs.statSync(filedir + '/' + filename);
        if(file.isDirectory()) {
            modules.push(filename);
        }
    }
    response.json(modules);
});

router.post('/module/save', function(request, response) {
    var filepath = path.join(__dirname, config.mockDir, request.body.module);
    logger.info(request, 'Create module: %s', filepath);
    if(!fs.existsSync(filepath)) {
        fs.mkdirSync(filepath);
    }
    response.json(true);
});

router.post('/module/rename', function(request, response) {
    var params = request.body;
    var oldPath = path.join(__dirname, config.mockDir, params.oldModule),
        newPath = path.join(__dirname, config.mockDir, params.newModule);
    logger.info(request, 'Rename module: %s=>%s', oldPath, newPath);
    if(fs.existsSync(newPath)) {
        response.json(false);
    } else {
        fs.renameSync(oldPath, newPath);
        response.json(true);
    }
});

router.post('/module/delete', function(request, response) {
    var filepath = path.join(__dirname, config.mockDir, request.body.module);
    logger.info(request, 'Delete module: %s', filepath);
    deleteAllSync(filepath);
    response.json(true);
});

router.post('/data/tree', function(request, response) {
    var params = request.body;
    var nodes = {
        name: 'MOCK',
        open: true,
        root: true,
        leaf: false
    };
    readDirSync(path.join(__dirname, config.mockDir, params.module), nodes);
    response.json(nodes);
});

router.post('/data/save', function(request, response) {
    var params = request.body;
    if (params.type == 'folder') {
        var filepath = path.join(__dirname, config.mockDir, params.module, params.folder);
        logger.info(request, 'Create directory: %s', filepath);
        if(fs.existsSync(filepath)) {
            response.json(false);
        } else {
            fs.mkdirSync(filepath);
            response.json(true);
        }
    } else if (params.type == 'file') {
        var filepath = path.join(__dirname, config.mockDir, params.module, params.folder + '/' + params.file);
        logger.info(request, 'Update file: %s', filepath);
        if(params.created && fs.existsSync(filepath)) {
            response.json(false);
        } else {
            fs.writeFileSync(filepath, params.data, { flag: 'w', encoding: 'utf-8' });
            response.json(true);
        }
    }
});

router.post('/data/rename', function(request, response) {
    var params = request.body;
    var oldPath = path.join(__dirname, config.mockDir, params.module, params.oldPath),
        newPath = path.join(__dirname, config.mockDir, params.module, params.newPath);
    logger.info(request, 'Rename path: %s=>%s', oldPath, newPath);
    if(fs.existsSync(newPath)) {
        response.json(false);
    } else {
        fs.renameSync(oldPath, newPath);
        response.json(true);
    }
});

router.post('/data/delete', function(request, response) {
    var params = request.body;
    var filepath = path.join(__dirname, config.mockDir, params.module, params.path);
    logger.info(request, 'Delete path: %s', filepath);
    deleteAllSync(filepath);
    response.json(true);
});

router.post('/data/copy', function(request, response) {
    var params = request.body;
    var source = path.join(__dirname, config.mockDir, params.module, params.source);
    var target = path.join(__dirname, config.mockDir, params.module, params.target);
    logger.info(request, 'Copy data: %s=>%s', source, target);
    if (params.type == 'folder') {
        params.ignore.root = true;
        copyFolder(source, target, params.ignore);
    } else if (params.type == 'file') {
        var filepath = path.dirname(target);
        if(!fs.existsSync(filepath)) {
            mkdirsSync(filepath);
        }
        copyFile(source, target, params.ignore);
    }
    response.json(true);
});

router.post('/test/send', function(request, response) {
    var params = request.body;
    var options = {
        url: params.url,
        method: params.method,
        headers: {
            'encoding': 'UTF-8',
            'Connection': 'keep-alive',
            'Cookie': request.headers.cookie
        },
        timeout: 30000 // 30s
    };
    if (params.data != null) {
        options['json'] = true;
        options['body'] = params.data;
    }
    httpRequest(options).on('error', function(error) {
        console.log(error);
    }).pipe(response);
});

router.post('/test/request', multipart(), function(request, response) {
    var url = request.body['__url'],
        method = request.body['__method'],
        headers = JSON.parse(request.body['__headers']),
        cookies = JSON.parse(request.body['__cookies']),
        body = request.body['__body'];

    var options = {
        url: url,
        method: method,
        headers: {
            'encoding': 'UTF-8',
            'Connection': 'keep-alive',
             // cookies
            'Cookie': reformCookies(request.headers.cookie, cookies)
        },
        timeout: 60000 // 60s
    };

    // headers
    for (var name in headers) {
        options.headers[name] = headers[name];
    }

    // body
    if (method != 'GET' && method != 'HEAD') {
        switch (headers['Content-Type']) {
            case 'multipart/form-data':
                var formData = {};
                if (body) {
                    var fields = JSON.parse(body);
                    for (var name in fields) {
                        formData[name] = fields[name];
                    }
                }
                if (request.files) {
                    for (var name in request.files) {
                        var file = request.files[name];
                        formData[name] = {
                            value: fs.createReadStream(file.path),
                            options: {
                                filename: file.originalFilename,
                                contentType: file.type
                            }
                        };
                    }
                }
                options['formData'] = formData;
                break;
            case 'application/json':
                options['json'] = true;
                if (body) {
	                options['body'] = JSON.parse(body);
                }
                break;
            case 'application/x-www-form-urlencoded':
                if (body) {
	                options['form'] = JSON.parse(body);
                }
                break;
            case 'text/plain':
            case 'application/javascript':
            case 'application/xml':
            case 'text/xml':
            case 'text/html':
                options['body'] = body;
                break;
            default:
                options['body'] = body;
        }
    }

    httpRequest(options).on('error', function(error) {
        console.log(error);
        response.end('The server has disconnected or the connection has timed out.');
    }).on('response', function(response) {
        // set cookies
        response.headers['x-cookies'] = (response.headers['set-cookie'] || []).join('___');
    }).on('end', function() {
        // delete files
        if (request.files) {
            for (var name in request.files) {
                fs.unlinkSync(request.files[name].path);
            }
        }
    }).pipe(response);
});

function reformCookies(cookie, cookies) {
    if (Object.keys(cookies).length == 0) {
        return cookie;
    } else {
        var pattern = /([^=]+)=([^;]+);?\s*/g,
            result = null;
        while ((result = pattern.exec(cookie)) != null) {
            cookies[result[1]] = result[2];
        }
        // cookies to string
        var values = [];
        for (var name in cookies) {
            values.push(name + '=' + cookies[name]);
        }
        return values.join('; ');
    }
}

function copyFolder(source, target, ignore) {
    var copyable = ignore.root || ignore.folder;
    if (!fs.existsSync(target)) {
        copyable = true;
        mkdirsSync(target);
    }
    if (copyable) {
        ignore.root = false;
        var files = fs.readdirSync(source);
        for (var i in files) {
            var filename = files[i];
            var curpath = source + '/' + filename;
            var tgtpath = target + '/' + filename;
            if(fs.statSync(curpath).isDirectory()) {
                copyFolder(curpath, tgtpath, ignore);
            } else {
                copyFile(curpath, tgtpath, ignore);
            }
        }
    }
}

function mkdirsSync(dirname) {
    if (fs.existsSync(dirname)) {
        return true;
    } else {
        if (mkdirsSync(path.dirname(dirname))) {
            fs.mkdirSync(dirname);
            return true;
        }
    }
}

function copyFile(source, target, ignore) {
    if (!fs.existsSync(target) || ignore.file) {
        fs.createReadStream(source).pipe(fs.createWriteStream(target));
    }
}

function deleteAllSync(filepath) {
    if(fs.existsSync(filepath)) {
        if(fs.statSync(filepath).isDirectory()) {
            var files = fs.readdirSync(filepath);
            for (var i in files) {
                var filename = files[i];
                var curpath = filepath + '/' + filename;
                if(fs.statSync(curpath).isDirectory()) {
                    deleteAllSync(curpath);
                } else {
                    fs.unlinkSync(curpath);
                }
            }
            fs.rmdirSync(filepath);
        } else {
            fs.unlinkSync(filepath);
        }
    }
}

function readDirSync(filedir, srcnode) {
    var children = [];
    var files = fs.readdirSync(filedir);
    for (var i in files) {
        var filename = files[i];
        var subnode = {};
        subnode.name = filename;
        var file = fs.statSync(filedir + '/' + filename);
        if(file.isDirectory()) {
            subnode.leaf = false;
            readDirSync(filedir + '/' + filename, subnode);
        } else {
            subnode.leaf = true;
        }
        children.push(subnode);
    }
    srcnode.children = children;
}

module.exports = router;
